![]() |
|
|||||
Das AWT von Java 1.0 besteht nur aus heavyweight Components. Damit wurde das Aussehen z.B. einer Schaltfläche oder Liste fest an Windows, Motif oder Macintosh gebunden. Bei Swing wird es als Vorteil angesehen, jederzeit auf einer Plattform alle anderen Look-and-Feels emulieren zu können. Swing: awaiting the GigaHertz-World Die Kehrseite ist klar. Swing ist äußerst ressourcenintensiv und selbst auf schnellen CPUs nicht sehr reaktiv. Dies ist der Preis der flexiblen Konzeption, die sich in Hunderten von Klassen mit einem komplexen Beziehungsgeflecht und Mustern kristallisiert. 15.3 Top-Level-Container
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Die Top-Level-Container im »alten« AWT sind Applet, Dialog, Frame und Window, die in Swing JApplet, JDialog, JFrame und JWindow.3
Im AWT waren allerdings gewisse Ungereimtheiten enthalten, die einer einfachen Erweiterung entgegenstanden. Unter anderem wurden Menü-Komponenten nicht von Component abgeleitet.
JComponent:
Basisklasse in Swing
Eine weitere Schwierigkeit besteht darin, dass in Swing alle Komponenten von JComponent abgeleitet werden, um die erweiterten Eigenschaften zu nutzen.
Natürlich bilden die Top-Level-Container von Swing eine Ausnahme, da sie bereits von ihren AWT-Pendants abgeleitet wurden. Also muss zwangsläufig4 ein einzelnes inneres Objekt JRootPane in jedem Root-Container von Swing erzeugt werden, das sich dann - der neuen Swing-Hierarchie folgend - von JComponent ableitet.
Damit sieht die Klassen-Hierarchie nicht mehr ganz so einfach aus (siehe Abb.15.2).
Klassen-Hierarchie der Root-Container
|
Interessant an der Hierarchie ist, dass alle Swing-Komponenten auch Container sind. Aber nur wenige werden tatsächlich als Container benutzt.
JInternalFrame:
virtueller Top-Level-Container
Des Weiteren gibt es noch einen lightweight Container JInternalFrame, der sich wie ein Top-Level-Container verhält, allerdings nicht als äußeres Fenster verwendet werden kann. Da er von keinem AWT-Container abgeleitet ist, ist JInternalFrame Subklasse von JComponent.
Zusammen mit JDesktopPane, einem Container für JInternalFrame, unterstützt somit Swing auch MDI (Multiple Document Interface), d.h., es können komplette Fenster in Fenster eingebettet werden.
Die fünf Root-Container, d.h. die vier Top-Level-Container und JInternalFrame, enthalten genau eine Instanz von JRootPane, die automatisch erzeugt wird.
JRootPane =
glassPane + layeredPane
JRootPane ist ein Container mit einem festen Aufbau. Er setzt sich aus einer Komponente, glassPane, und layeredPane, Instanz der Klasse JLayeredPane, zusammen.
glassPane ist per Default eine Instanz von JPanel, liegt über den anderen Komponenten und fängt die Ereignisse der Maus ab. Allerdings könnte es als beliebige Komponente auch zum Zeichnen eingesetzt werden.
Wie bereits der Name sagt, wird JLayeredPane dazu benutzt, mehrere Komponenten, die sich möglicherweise überlappen, in einer Reihenfolge anzuordnen (siehe 15.3.2).
layeredPane =
menuBar + contentPane
Die layeredPane enthält in JRootPane allerdings nur die beiden Komponenten menuBar, Instanz von JMenuBar, und einen Container contentPane. Der Container contentPane ist per Default ein JPanel und nimmt die eigentlichen Komponenten des Fensters auf (siehe Abb. 15.3).
|
Von allen aufgeführten Komponenten ist nur die Menüleiste optional.
Das Interface RootPaneContainer wird von allen Root-Containern implementiert und enthält Zugriffs-Methoden auf JRootPane und seine internen Komponenten (mit Ausnahme der optionalen Menüleiste).
interface RootPaneContainer { JRootPane getRootPane(); Component getGlassPane(); JLayeredPane getLayeredPane(); Container getContentPane();
void setGlassPane (Component glassPane); void setLayeredPane(JLayeredPane layeredPane); void setContentPane(Container contentPane); }
Da die Ereignisbehandlung erst nachfolgend behandelt wird, reagiert der Top-Level-Container JFrame einzig auf das Ereignis »Schließen des Fensters« mit Beenden der Applikation (siehe Abb. 15.4).
JFrame-Beispiel:
JMenuBar + JLabel
package kap15;
import java.awt.*; import java.awt.event.*; import javax.swing.*;
public class Test { public static void main(String[] args) {
// neues Top-Level-Fenster anlegen JFrame jf= new JFrame();
// Ereignis "Schließen der Frame" beendet Applikation jf.addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } });
// eine Menüleiste hinzufügen JMenuBar jbar= new JMenuBar(); jf.getRootPane().setJMenuBar(jbar);
// einen Menüeintrag hinzufügen JMenu jmenu= new JMenu("Menü1"); jbar.add(jmenu);
// zwei Einträge zu Menü1 hinzufügen jmenu.add("1. Eintrag"); jmenu.add("2. Eintrag");
//auch möglich: JPanel jp= (JPanel) jf.getContentPane(); Container jp= jf.getContentPane();
// Eine Text in den contentPane einfügen jp.add(new JLabel("--- Komponente im contentPane ---"));
// Anzeigen des Fensters jf.pack(); jf.setVisible(true); // show() ist deprecated! } }
Alle Container verfügen über eine Methode add(), um andere Komponenten aufzunehmen.
|
GlassPane-Variation: Im folgenden Test wird eine Komponente in glassPane eingefügt. Da glassPane über menuBar und contentPane liegt, werden die Komponenten in glassPane zum Schluss gezeichnet und überdecken eventuell die darunter liegenden (siehe Abb. 15.5).
|
Subklasse von JFrame:
reagiert auf Ereignis »Schließen«
Damit nicht immer wieder der Code für »Schließen des Fensters« hinzugefügt werden muss, wird eine Subklasse EJFrame von JFrame abgeleitet.
class EJFrame extends JFrame { public EJFrame() { addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); } }); } }
Komponenten im glassPane überdecken alles
public class Test { public static void main(String[] args) { JFrame jf= new EJFrame(); // Kommentar siehe unten
JPanel jg= (JPanel) jf.getGlassPane(); jg.add(new JLabel("--- Komponente im glassPane ---")); jg.setVisible(true); // sonst unsichtbar Container jp= jf.getContentPane(); jp.add(new JLabel("--- Komponente im contentPane ---")); jf.setSize(250,70); // Fenstergröße manuell setzen jf.setVisible(true); } }
Anordnung mittels Layers und Positionen
Der Container layeredPane von JRootPane ordnet alle Komponenten, die man mittels seiner speziellen add()-Methoden einfügt, in einem Stack an, sofern sie sich überlappen. Hierzu benutzt layeredPane Ebenen bzw. Layer und innerhalb der Ebenen wieder Positionen.
| Layer werden anhand Integer-Objekten geordnet, wobei Layer mit einer größeren Integer-Zahl über denen mit einer kleineren liegen. |
| Komponenten innerhalb derselben Layer werden anhand einer int-Position geordnet, wobei die Komponente mit der Position 0 zuoberst liegt. Komponenten mit einer größeren Position liegen dann unter denen mit einer kleineren. |
In der nächsten Test-Variation werden Schaltflächen zur besseren Sichtbarkeit in verschiedenen Ebenen und Positionen angeordnet. Da JLayeredPane keinen Layout-Manager benutzt, müssen die Komponenten ihre Größe und Pixel-Position selbst setzen.
class MyButton extends JButton { private static int num= 0;
public MyButton (JLayeredPane lp, int layer, int pos) { setBounds(num*30,num*30,150,50); // x,y, Breite, Höhe setForeground(Color.black); // Schrift schwarz setText("Nr. "+ num++ +" Layer "+ layer+", Pos. "+ pos); lp.add(this,new Integer(layer),pos); // anordnen } }
Anordnung von Schaltflächen mittels Layer- und Positionsangabe
public class Test { public static void main(String[] args) { JFrame jf= new EJFrame(); // EJFrame wie oben
JLayeredPane jlp= jf.getLayeredPane();
new MyButton(jlp,3,1); // obere Layer new MyButton(jlp,1,1); // untere Layer, untere Position new MyButton(jlp,1,0); // untere Layer, obere Position new MyButton(jlp,2,1); // mittlere Layer
jf.setSize(300,180); jf.setVisible(true); } }
Die Anordnung entspricht dann der in Abb. 15.6 dargestellten.
Layer und Positionen am Beispiel
|
Die beiden Komponenten contendPane und menuBar werden in einer vordefinierten Ebene JLayeredPane.FRAME_CONTENT_LAYER (der zugehörige Wert ist -30000) eingeordnet, d.h. im Normalfall ganz unten.
Die oberste vordefinierte Ebene JLayeredPane.DRAG_LAYER (Wert: 400) ist dann Drag-Komponenten vorbehalten, die beim Ziehen über die Oberfläche immer über den anderen erscheinen müssen.
JWindow: Fenster ohne Dekoration
JWindow, direkt abgeleitet von Window, ist ein rudimentäres Fenster ohne Dekoration (Fensterleiste, Größenanpassung, Schließen).
Damit sind die Einsatzmöglichkeiten recht gering und in der Regel auf Einsätze wie z.B. Startfenster begrenzt.
Zentrieren des Fensters auf dem Bildschirm
Diese Variation der Test-Klasse verwendet eine ScreenUtil-Klasse zur Zentrierung eines Windows auf dem Bildschirm. Des Weiteren werden der Font und die Größe des Textes manipuliert.
getDefaultToolkit(),
getScreenSize()
final class ScreenUtil { private ScreenUtil() {} public static void center(Window w, int width, int height) { Dimension scr= Toolkit.getDefaultToolkit().getScreenSize(); w.setBounds((scr.width-width)/2,(scr.height-height)/2, width,height); } }
JWindow:
ein ideales Startfenster
public class Test { public static void main(String[] args) { JWindow jw= new JWindow(); JLabel jl= new JLabel();
JLabel, Font:
setText(), setFont()
// Font setzen: Font-Name, Stil, Größe jl.setFont(new Font("Bookman",Font.BOLD+Font.ITALIC,18)); jl.setText("Startfenster");
// ohne Layout-Manager: Größe und Position selbst setzen jw.getContentPane().setLayout(null); jl.setBounds(20,10,180,50);
jw.getContentPane().add(jl);
ScreenUtil.center(jw,150,70); jw.setVisible(true); // ... weitere Aktionen, Window schließen etc. } }
|
Toolkit-Klasse
mit nützlichen Operationen
Die abstrakte Klasse java.awt.Toolkit liefert mit Hilfe der statischen Methode getDefaultToolkit() eine Instanz von sich, die Informationen und Anpassungen an die Plattform/Hardware bereitstellt.
Die Toolkit-Instanz liefert u.a. Bildschirmauflösung und -größe, bietet Cursor-Anpassungen und Font-Informationen.
| Die Klasse Toolkit sollte selten direkt genutzt werden, da sie die Plattformunabhängigkeit nicht unbedingt fördert. |
Die Klasse java.awt.Font erlaubt alle Font-Familiennamen im Konstruktor, die das System bietet. Im zweiten Argument wird der Stil (als Konstanten Font.PLAIN, BOLD, ITALIC oder BOLD+ITALIC) und im dritten die Größe in Punkten übergeben.
JFrame mit plattformspezifischer
Dekoration
Eine Instanz von JFrame oder Frame ist normalerweise das Hauptfenster einer Applikation. Beide Top-Level-Container bieten per Default plattformspezifisch einen Rahmen, Titelleiste mit Minimierungs-, Maximierungs- und Schließ-Schaltflächen.
| Die Superklasse Frame aus dem AWT enthält kein JRootPane, d.h., in Frame werden die Komponenten direkt mit add() eingefügt. |
JFrame enthält dagegen wie alle Top-Level-Container von Swing ein JRootPane und lässt das Einfügen von Komponenten nur in contentPane zu. Der Versuch, Komponenten in JFrame direkt mit add() einzufügen, endet mit einer Ausnahme.
Wie bereits bekannt, reagiert JFrame nicht automatisch auf das Ereignis »Schließen« mit dem Beenden der Applikation, sondern macht nur das Fenster unsichtbar.5
Frame, JFrame:
subtile Unterschiede
Das folgende Code-Fragment öffnet ein AWT- und ein Swing-Fenster. Der Code für beide ist sehr ähnlich, jedoch gibt es subtile Unterschiede:
Frame f= new Frame(); // oder direkt Frame("Titel") f.setSize(100,100); f.setTitle("Titel"); f.setBackground(Color.green); // direkt für die Frame f.add(new Label("hallo")); // direkt in die Frame f.setVisible(true); // Frame lässt sich per Default nicht schließen!
JFrame jf= new JFrame(); // oder direkt JFrame("Titel") jf.setSize(100,100); jf.setTitle("Titel");
JFrame:
setBackground(), add()
// jf.setBackground(Color.green); wäre möglich, // aber nutzlos, da contentPane alles überdeckt. jf.getContentPane().setBackground(Color.green); jf.getContentPane().add(new JLabel("hallo")); jf.setVisible(true); // JFrame wird per Default beim Schließen nur unsichtbar!
Frame reicht
für einfache Java-Apps
| Für einfache Oberflächen und zur Schonung der Ressourcen reicht in der Regel ein Frame. Haben Flexibilität oder die Nutzung neuer Fähigkeiten Priorität, ist JFrame vorzuziehen. |
WindowConstants:
Anpassen des Schließverhaltens
JFrame zusammen mit JDialog und JInternalFrame implementieren das Interface WindowConstants, welches nur drei Konstanten definiert, um das Verhalten beim »Schließen« anzupassen.
public interface WindowConstants { int DO_NOTHING_ON_CLOSE= 0; // wie Frame int HIDE_ON_CLOSE= 1; // default int DISPOSE_ON_CLOSE= 2; // Ressourcen freigeben }
Ein bestimmtes Schließverhalten wird dann wie folgt gesetzt:
jf.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
UIManager:
L&F innerhalb eines Top-Level-Containers
Für die grafische Darstellung der Komponenten in einem Top-Level-Container wie JFrame stehen zumindest zwei bzw. drei verschiedene Oberflächen zur Verfügung.
Im folgenden Code-Fragment werden die verschiedenen L&F angezeigt und die erste gesetzt:
UIManager.LookAndFeelInfo[] lafArr= UIManager.getInstalledLookAndFeels();
for (int i= 0; i<lafArr.length; i++) System.out.print(lafArr[i].getName()+" ");
try { UIManager.setLookAndFeel(lafArr[0].getClassName()); } catch (Exception e) { System.out.println(e); }
JDialog:
spezialisiert auf Dialoge
Die Klasse JDialog ist ein Top-Level-Container, der auf Dialoge spezialisiert ist. Er hat deshalb auch keine Minimierungs- bzw. Maximierungs- Schaltflächen, sondern nur eine Schließ-Schaltfläche.
Des Weiteren gibt es einen Konstruktor, der das Fenster im modalen Modus öffnet, d.h., kein anderes Fenster derselben Applikation kann aktiv sein, bevor dieser Dialog nicht geschlossen wird.
Nachfolgend wird das L&F auf Motif gesetzt, der Dialog im Hauptfenster zentriert und das Schließverhalten auf DISPOSE_ON_CLOSE gesetzt.
try { UIManager.setLookAndFeel( "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); } catch (Exception e) { System.out.println(e); }
JFrame jf= new JFrame("JFrame"); ScreenUtil.center(jf,200,150); // siehe Beispiel in JWindow jf.setVisible(true);
JDialog jd= new JDialog(jf,"JDialog",true); // true= modal jd.setSize(100,50); jd.setLocationRelativeTo(jf); // zentiert in jf
jd.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); jd.show(); // wie setVisible(true)
|
| Die Superklasse Component im AWT enthält eine Methode show(), die als deprecated markiert ist. In der Regel sollte setVisible(true) verwendet werden. |
Allerdings wird in einigen Subklassen wie z.B. Dialog die Methode show() wieder gültig überschrieben und wie in JInternalFrame mit zusätzlicher Funktionalität versehen.
JOptionPane:
nützliche Dialog-Muster
Kurze Mitteilungen oder Abfragen sollten eine einheitliche Struktur aufweisen. Immer wieder den gleichen JDialog-Code zu schreiben, ist lästig und redundant.
Hier kommt die Klasse JOptionPane zur Hilfe. Sie ist direkt von JComponent abgeleitet und definiert bzw. implementiert eine Palette brauchbarer Dialog-Muster mit Hilfe von JDialog oder JInternalFrame (siehe Abb. 15.9).
Visuelles Äußeres von JOptionPane
|
JOptionPane bietet Dialog-Schablonen
JOptionPane stellt für verschieden Aufgaben statische Methoden und Konstanten zur Verfügung, die für die meisten Aufgaben ausreichen. Sollte eine Instanz von JOptionPane wiederverwendet werden, können sie auch mit Hilfe passender Konstruktoren angelegt werden.
Die statischen Methoden decken vier Typen von Dialogen ab:
| Bestätigen/Confirm: Frage, Fehler, Warnung etc. mit diversen Schaltflächen. |
| Eingabe/Input: Zeile, Liste etc. mit zwei Schaltflächen OK und Cancel. |
| Mitteilung/Message: Mitteilung mit der Schaltfläche OK. |
| Option: Beliebige Daten mit Schaltflächen. |
Drei statische Methoden sollen an kleinen Code-Fragmenten demonstriert werden.
Der folgende Dialog hat kein übergeordnetes Fenster (parentComponent ist null), hat einen String als Mitteilungs-Objekt, einen Titel, die Schaltfläche OK und ein Icon, das einen Fehler symbolisieren soll (siehe Abb. 15.10). Ist das übergeordnete Fenster null, wird der Dialog im Bildschirm zentriert.
System.out.println( JOptionPane.showConfirmDialog (null, "Mitteilung mit CLOSED_OPTION","ConfirmDialog", JOptionPane.CLOSED_OPTION, JOptionPane.ERROR_MESSAGE) ); // :: 0
Bestätigung mit JOptionPane im
Motif-L&F
|
Die Methode showInputDialog() akzeptiert ein Array von Objekten als Auswahl, wobei als letztes Argument das per Default selektierte Objekt übergeben werden kann.
Die Darstellung der Objekte erfolgt in diesem Fall in einer JComboBox (siehe Abb. 15.11).
System.out.println( JOptionPane.showInputDialog (null, "Auswahliste","InputDialog", JOptionPane.QUESTION_MESSAGE, null, new String[] { "1.","2.","3.","4.","5.","6."}, "3.") ); // Bei Cancel :: null // Bei OK :: 3.
Auswahlliste mit JOptionPane im
Motif-L&F
|
Im folgenden Beispiel wird ein Hauptfenster erzeugt, in dem das Option-Dialog-Fenster zentriert und modal geöffnet wird.
Die Darstellung der Objekte erfolgt in diesem Fall mit Schaltflächen. Bei einer Auswahl wird als Rückgabewert der Array-Index zurückgegeben, ansonsten der Wert -1 (siehe Abb. 15.12).
try { UIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } catch (Exception e) { System.out.println(e); }
JFrame jf= new JFrame("JFrame"); jf.show();
String[] sarr= new String[] { "1.","2.","3.","4.","5.","6."}; System.out.println( JOptionPane.showOptionDialog(jf, "Optionsauswahl","OptionDialog", JOptionPane.DEFAULT_OPTION, OptionPane.QUESTION_MESSAGE,null,sarr,null) );
Optionen mit JOptionPane in einem Hauptfenster im Windows-L&F
|
Die beiden null-Werte in showOptionDialog() bedeuten »kein eigenes Icon« und »keine Default-Selektion«.
Layout-Manager:
Design- Kompromisse
Container verfügen per Default über einen Layout-Manager, der die Aufgabe hat, die Komponenten, die hinzugefügt werden, im Inneren nach einem Schema anzuordnen.
| Layout-Manager sind immer ein Kompromiss zwischen optimalem Design und Flexibilität. |
Kennt man alle Umgebungs-Variablen wie Fenster-, Schrift- oder Randgrößen, führt eine exakte Positionierung und Größe der Komponenten zu einem optimalen Design der Oberfläche.
Layout-Manager: Anpassungen zur Laufzeit
Da die Oberfläche allerdings meistens auf unterschiedlichen Plattformen bzw. unterschiedlicher Hardware laufen soll, muss man die Arbeit einem Layout-Manager überlassen, der dann zur Laufzeit versucht, die Lage der Komponenten an die Umgebung (sub)optimal anzupassen.
Eigenschaften von Layout-Managern
| können für die meisten Container mit setLayout() frei gewählt werden. |
| können auf null gesetzt werden, was bedeutet, dass man Position und Größe für jede Komponente selbst setzen muss, z.B. mit setBounds().6 |
| können selbst entwickelt werden, d.h., sie müssen das Interface LayoutManager oder das Subinterface LayoutManager2 implementieren. |
| werden vom Container bei allen Änderungen, die das Layout berühren, wie z.B. beim Einfügen und Entfernen von Komponenten aufgerufen. |
Das AWT und Swing definieren bereits einige häufiger benötigte Layout-Schemata (siehe Abb. 15.13).
AWT- und Swing- Layout-Manager: interface-basierende Hierarchie
|
Der LayoutManager2 erweitert also das ursprüngliche Interface um weitere Methoden, um unter anderem Constrains - zusätzliche Beschränkungen - spezifizieren zu können.
Größeneigenschaften von JComponent
Jede Swing-Komponente JComponent hat eine minimale, maximale und bevorzugte Größe. Sie kann - sofern erforderlich - mit setMinimumSize(), setMaximumSize() bzw. setPreferredSize() gesetzt werden.
Die letztgenannte bevorzugte Größe (Typ Dimension) gibt an, wie groß die Komponente sein sollte, um alle internen Elemente optimal darzustellen und ist deshalb für Layout-Manager von besonderer Bedeutung.
Bei einer Schaltfläche JButton berechnet sich die bevorzugte (und minimale) Größe z.B. aus der Textgröße und -länge, dem Textabstand zum Rand und der Randgröße selbst. Sie wird automatisch berechnet, wobei das Maximum praktisch unbegrenzt ist.
| Lassen es die Umgebung und das Layout-Schema zu, wird der Layout-Manager die bevorzugte Größe der Komponente einhalten. |
Versuche, z.B. mittels setSize() oder setBounds(), die Größe der Komponenten selbst zu bestimmen, scheitern immer daran kläglich, dass der Layout-Manager das letzte Wort hat.
Schemata der AWT-Layout-Manager
| Layout-Manager | Layout-Schema |
| FlowLayout | Komponenten werden mit ihrer bevorzugten Größe wie Wörter in Zeilen angeordnet, wobei diese umbrechen können. Wie bei Text kann die Zentrierung mit LEFT, RIGHT oder CENTER angegeben werden. |
| GridLayout | Komponenten werden ohne Rücksicht auf ihre bevorzugte Größe in die Zellen einer Matrix eingeordnet. |
| BorderLayout | Komponenten können in die Kompass-Bereiche NORTH, SOUTH, EAST, WEST oder in das Zentrum CENTER eingefügt werden. Die bevorzugte Größe wird abhängig vom Bereich berücksichtigt. |
| CardLayout | Jede Komponente ist so groß wie der Container und zu einem Zeitpunkt ist immer nur eine Komponente sichtbar. |
| GridBagLayout | Ein GridLayout, in dem Reihen und Spalten unterschiedliche Höhen oder Breiten haben können. Komponenten können eine oder mehrere Zelle(n) einnehmen, wobei die Informationen beim add() an eigene Instanzen der Klasse GridBagConstraints übergeben werden. |
Alle in Swing definierten Layout-Manager sind für spezifische Komponenten definiert, wobei das BoxLayout allerdings auch generell nützlich sein kann.
Schemata der Swing-Layout-Manager
| Layout-Manager | Layout-Schema |
| ScrollPaneLayout | Spezialisiert für den JScrollPane-Container, definiert dieses Layout neun Bereiche, in die ein »Lauf«-Bereich eingeteilt werden kann. Neben dem eigentlichen Laufbereich JViewPort sind dies die vier Ecken, die Zeilen- und Spaltenköpfe und die vertikalen und horizontalen Rollbalken. |
| ViewportLayout | Spezialisiertes Layout für JViewPort (siehe oben). |
| BoxLayout | Dieses Layout, benutzt vom Box-Container, fügt die Komponenten entweder horizontal von links nach rechts oder vertikal von oben nach unten in einer Reihe/Box ein. Dabei beachtet es im Gegensatz zum GridLayout die bevorzugte Größe der Komponenten. |
| OverlayLayout | Überlagert Komponenten anhand eines Ausrichtungspunkts und wird u.a. vom JMenuItem für Text und Icons benutzt. |
Ausgangspunkt für einen optimalen Entwurf ist ein idealer Bildschirm mit exakt positionierten Komponenten idealer Größe, mit anderen Worten ein typisches XY-Design.
Es gibt - bis auf triviale Anordnungen - keinen einzelnen Layout-Manager, den man der contentPane eines Top-Level-Fensters zuweisen könnte, um dieses XY-Design nachzubilden. Deshalb gilt:
Kunst der Komposition von Layout-Managern
| Die Kunst besteht darin, eine geeignete Komposition von passenden Layouts zu finden, die dem idealen Design am nächsten kommt. |
Hierzu muss man sicherlich die Wirkung der o.a. Layouts im Detail kennen, um Container mit passenden Layout-Managern entsprechend zusammenzusetzen.
Im folgenden Code-Fragment wird die Wirkung eines Border-Layouts mit einem eingebetteten Container mit Flow-Layout demonstriert.
Border-Layout mit internem Flow-Layout
JFrame jf= new EJFrame("Border/Flow-Layout"); jf.setSize(200,200); // siehe Erklärung
Container cp= jf.getContentPane();
//cp.setLayout(new BorderLayout()); überflüssig, da default! cp.add(new JButton("Nord"),BorderLayout.NORTH); cp.add(new JButton("Süd"),BorderLayout.SOUTH); cp.add(new JButton("West"),BorderLayout.WEST); cp.add(new JButton("Ost"),BorderLayout.EAST);
Container jp= new JPanel();
//jp.setLayout(new FlowLayout()); überflüssig, da default! for (int i= 0; i<9; i++) jp.add(new JButton("Nr. "+(i+1)));
jp.setSize(100,50); // ohne Wirkung! cp.add(jp,BorderLayout.CENTER);
//jf.pack(); siehe Kommentar unten! jf.setVisible(true);
Reaktionen von Border- und Flow-Layout zur Laufzeit
|
Erklärung: Die Größe des Top-Level-Fensters lässt im Center-Bereich nur eine Schaltfläche pro Zeile des Flow-Layouts zu (Abb. 15.14 oben links).
Sollte jf.pack() in der vorletzten Zeile ausgeführt werden, wird die Größe des Top-Level-Fensters anhand der inneren Komponenten bestimmt, d.h., jp.setSize() in der zweiten Zeile wird wirkungslos (Abb. 15.14 unten).
Eine Größenänderung des Top-Level-Fensters durch den Benutzer führt dann zu diversen anderen Anordnungen (Abb. 15.14 oben rechts).
CardLayout:
Karten nebeneinander angeordnet
Bei Containern mit CardLayout kann mittels add(Component c, Object constraints) jeweils eine Komponente pro Karte eingefügt werden. Das Objekt constraints muss dabei ein String sein, der die Aufgabe hat, die Karte zu identifizieren (kann aber auch "" gesetzt werden).
CardLayout enthält dann fünf Methoden, die Karten zu wechseln:
public void first (Container parent); public void last (Container parent); public void next (Container parent); public void previous(Container parent); public void show (Container parent, String name);
Die contendPane wird nachfolgend auf CardLayout gesetzt und es werden drei Bilder eingefügt. Mittels next() werden dann die Karten gewechselt.
|
JFrame jf= new EJFrame("3 Cards"); Container cp= jf.getContentPane(); CardLayout cl; cp.setLayout(cl= new CardLayout()); JLabel im; for (int i= 0; i<3; i++) { im= new JLabel(); // langweilige Version, siehe GridLayout im.setIcon(new ImageIcon("C:/Scan/bild"+i+".gif")); cp.add(im,"image"+i); } jf.pack(); jf.setVisible(true); while (true) { try { Thread.sleep(2000); } catch (Exception e) {} cl.next(cp); // wechselt am Ende zur ersten Karte }
GridLayout:
grafische Matrix mit einheitlichen Zellen
Wie bei einer Matrix, wird bei der Anlage eines GridLayouts in der Regel die Anzahl der Zeilen und Spalten angegeben.
Da es sich um eine »grafische« Matrix handelt, können die Abstände in Pixel zwischen den Zellen angegeben werden. Die beiden ersten Konstruktoren sind Spezialfälle des letzten unten angeführten, d.h. rufen diesen über this(..) auf.
public GridLayout(); // this(1, 0, 0, 0); public GridLayout(int rows, int cols); // this(rows,cols,0,0) public GridLayout(int rows, int cols, int hgap, int vgap);
Das folgende Code-Fragment ist nur eine einfache Variation des Beispiels in CardLayout.
|
JFrame jf= new EJFrame("GridLayout"); Container cp= jf.getContentPane();
Bildübergabe an
JLabel-Konstruktor
cp.setLayout(new GridLayout(2,2,10,10)); for (int i= 0; i< 3; i++) cp.add(new JLabel(new ImageIcon("C:/Scan/bild"+i+".gif"))); cp.add(new JButton("Schaltfläche")); jf.pack(); jf.setVisible(true);
JLabel kann im Konstruktor sofort ein Bild übergeben werden. Alle Komponenten bekommen die gleiche Größe, und zwar die der größten Komponente.
GridBagLayout:
große Flexibilität vs. Codier-Aufwand
Das GridBagLayout ist aufgrund der GridBagConstraints-Instanz, die bei add() mit jeder Komponente übergeben wird, sehr flexibel.
GridBagConstraints:
Steuerung mittels elf Parametern
| Der Aufwand für das Einfügen einer Komponente mit Hilfe eines GridBagLayout ist in der Regel größer als beim null-Layout. |
Mit Hilfe von elf Parametern(!), gekapselt in einer GridBagConstraints-Instanz, wird das Verhalten jeder einzelnen Komponente definiert.
public GridBagConstraints(); // setzt Default-Werte public GridBagConstraints(int gridx,int gridy, int gridwidth,int gridheight, double weightx,double weighty, int anchor, int fill, Insets insets, int ipadx,int ipady)
Übersicht über GridBagConstraints- Parameter
| Parameter | Bedeutung |
| gridx gridy |
Zeile/Spalte in der Matrix. Die Konstante RELATIVE (der Default-Wert) bedeutet bei x bzw. y rechts bzw. unter der letzten eingefügten Komponente. |
| gridwidth gridheight |
Anzahl der benötigten Zellen in der Zeile/Spalte (Default-Wert: 1,1). RELATIVE zeigt die vorletzte Komponente der Zeile bzw. Spalte an. REMAINDER bedeutet die letzte Komponente, die alle rest |